package eol.vm;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;

import eol.vm.Machine.ExecutionError;
import eol.vm.Value.StringValue;

/** Environments map variables to values
 * <p>Environments  are chained together in a stack, so if 
 * a variable is not found in this environment, we typically
 * defer to the next (parent) environment in the chain.
 *
 */
class Environment {
    ArrayList<StringValue> vars = new ArrayList<StringValue>() ;
    ArrayList<Value> vals = new ArrayList<Value>() ;
    Environment next  ;
    // invariant vars.size() == vals.size() 
    
    Environment( List<StringValue> vars, List<Value> vals, Environment next) {
    		this.vars.addAll(vars) ;
    		this.vals.addAll(vals) ; 
    		this.next = next ; }
    
    void print(PrintStream stream) {
    		stream.print("env: ") ;
    		for(int i = 0 ; i < vars.size() ; ++i ) {
    			stream.print( "(" + vars.get(i) + "->" + vals.get(i) + ") " ) ; }
			stream.println() ;
			if( next != null ) {
    			next.print( stream );
    		}
    }
    Value get( StringValue var ) throws ExecutionError {
    		// If the variable is mapped by this environment, get it.
    		for( int i = 0 ; i < vars.size() ; ++i ) {
    			if( vars.get(i).equals(var) ) return vals.get(i) ; }
    		// Otherwise, defer to the next environment in the chain.
    		if( next == null ) throw new ExecutionError("Fetch from unknown variable: " +var)  ; 
    		return next.get(var) ;
    }
    
    void set( StringValue var, Value val )  throws ExecutionError {
    		// If the variable is mapped by this environment, set it.
    		for( int i = 0 ; i < vars.size() ; ++i ) {
    			if( vars.get(i).equals(var) ) { vals.set(i, val) ; return ; }
    		}
    		
    		// Otherwise, defer to the next environment on the chain.
    		if( next == null ) throw new ExecutionError("Store to unknown variable: " +var)  ; 	
    		next.set(var, val) ;
    }
    
    void newVar( StringValue var, Value val )  throws ExecutionError {
			// If the variable is mapped by this environment, set it.
    		for( int i = 0 ; i < vars.size() ; ++i ) {
    			if( vars.get(i).equals(var) ) { vals.set(i, val) ; return ; }
    		}
    		// Otherwise, add it.
    		vars.add(var) ; vals.add( val ) ;
    }
}